# -*- coding: utf-8 -*-
import re
import sys
import os
import json
from datetime import datetime

sys.path.append(os.path.abspath(os.path.join('..', 'gl0906_common')))
sys.path.append(os.path.abspath(os.path.join('..', 'elftool')))
sys.path.append(os.path.abspath(os.path.join('..', 'hexfiletool')))

from gl0906_logger import gl0906_logger, redirect_output_to_log
from elf_map_a2l import gl0906_elf_parse
from hexfileshandle import dcheck_and_parse_blocks, dmerge_hex_data

src_file_dir = "./src_files"
generate_file_dir = "./generated"

g_a2l_text_front_content = '''/******************************************************************************
 *
 *  ASAP2 file:     SXXT_01_FBCM.a2l
 *
 * 
 *
 * Code generation for model "SXXT_01_FBCM".
 *
 * Model version              : 1.9
 * Simulink Coder version : 10.3 R2021a
 * C source code generated on : {ddd2}
 *
 * Target 
 selection: ert.tlc
 * Embedded hardware selection: NXP->Cortex-M4
 * Code generation objectives: Unspecified
 * Validation result: Not run
 *
 * (add additional file header information here)
 *
 ******************************************************************************/
ASAP2_VERSION  1 31     /* Version 1.31 */

/begin PROJECT ProjectName "Project Comment Goes Here"

  /begin HEADER "Header Comments go here"

  /end HEADER

  /begin MODULE ModuleName  "Module Comment Goes Here"

 /begin A2ML


      block "IF_DATA" taggedunion if_data {{

        "CANAPE_EXT" struct {{
          int;             /* version number */
          taggedstruct {{
            "LINK_MAP" struct {{
              char[256];   /* segment name */
              long;        /* base address of the segment */
              uint;        /* address extension of the segment */
              uint;        /* flag: address is relative to DS */
              long;        /* offset of the segment address */
              uint;        /* datatypValid */
              uint;        /* enum datatyp */
              uint;        /* bit offset of the segment */
            }};
            "DISPLAY" struct {{
              long;        /* display color */
              double;      /* minimal display value (phys)*/
              double;      /* maximal display value (phys)*/
            }};
            "VIRTUAL_CONVERSION" struct {{
              char[256];   /* name of the conversion formula */
            }};
          }};
        }};
        "CANAPE_MODULE" struct {{
          taggedstruct {{
            ("RECORD_LAYOUT_STEPSIZE" struct {{
              char[256];   /* name of record layout*/
              uint;        /* stepsize for FNC_VALUES */
              uint;        /* stepsize for AXIS_PTS_X */
              uint;        /* stepsize for AXIS_PTS_Y */
              uint;        /* stepsize for AXIS_PTS_Z */
              uint;        /* stepsize for AXIS_PTS_4 */
              uint;        /* stepsize for AXIS_PTS_5 */
            }})*;
          }};
        }};
        "CANAPE_ADDRESS_UPDATE" taggedstruct {{
          ("EPK_ADDRESS" struct {{
            char[1024];         /* name of the corresponding symbol in MAP file */
            long;               /* optional address offset */
          }})*;
          "ECU_CALIBRATION_OFFSET" struct {{
            char[1024];         /* name of the corresponding symbol in MAP file */
            long;               /* optional address offset */
          }};
          (block "CALIBRATION_METHOD" taggedunion {{
            "AUTOSAR_SINGLE_POINTERED" struct {{
              char[1024];         /* MAP symbol name for pointer table in RAM */
              long;               /* optional address offset */
              taggedstruct {{
                "ORIGINAL_POINTER_TABLE" struct {{
                  char[1024];    /* MAP symbol name for pointer table in FLASH */
                  long;          /* optional address offset */
                }};
              }};
            }};
            "InCircuit2" struct {{
              char[1024];         /* MAP symbol name for pointer table in RAM */
              long;               /* optional address offset */
              taggedstruct {{
                "ORIGINAL_POINTER_TABLE" struct {{
                  char[1024];    /* MAP symbol name for pointer table in FLASH */
                  long;          /* optional address offset */
                }};
                "FLASH_SECTION" struct {{
                  ulong;       /* start address of flash section */
                  ulong;       /* length of flash section */
                }};
              }};
            }};
          }})*;
          block "MAP_SYMBOL" taggedstruct {{
            "FIRST" struct {{
              char[1024];  /* symbol name of the corresponding segment in MAP file */
              long;        /* offset */
            }};
            "LAST" struct {{
              char[1024];  /* symbol name of the corresponding segment in MAP file */
              long;        /* offset */
            }};
            ("ADDRESS_MAPPING_XCP" struct {{
              char[1024];  /* symbol name of source range in MAP file */
              char[1024];  /* symbol name of destination range in MAP file */
            }})*;
          }};
          (block "MEMORY_SEGMENT" struct {{
            char[1024];         /* name of the memory segment */
            taggedstruct {{
              "FIRST" struct {{
                char[1024];  /* symbol name of the corresponding segment in MAP file */
                long;        /* offset */
              }};
              "LAST" struct {{
                char[1024];  /* symbol name of the corresponding segment in MAP file */
                long;        /* offset */
              }};
              ("ADDRESS_MAPPING_XCP" struct {{
                char[1024];  /* symbol name of source range in MAP file */
                char[1024];  /* symbol name of destination range in MAP file */
              }})*;
            }};
          }})*;
        }};
        "CANAPE_GROUP" taggedstruct {{
          block "STRUCTURE_LIST" (char[1024])*;
        }};

        "ASAP1B_CCP" taggedstruct {{
          (block "SOURCE" struct {{
            struct {{
              char[101];  /* Name of the DAQ-List (data acquisition list), measurement source*/
              int;  /* Period definition : Basic scaling unit in CSE*/
              long;  /* Period definition : Rate in Scaling Units*/
            }};
            taggedstruct {{
              "DISPLAY_IDENTIFIER" char[32];  /* display identifier of DAQ list*/
              block "QP_BLOB" struct {{
                uint;  /* Number of the DAQ-List 0..n*/
                taggedstruct {{
                  "LENGTH" uint;  /* Length of the DAQ-Liste, maximum number of the useable ODTs*/
                  "CAN_ID_VARIABLE" ;  /* CAN-Message-ID is variable*/
                  "CAN_ID_FIXED" ulong;  /* CAN-Message-ID of the DTOs if fixed*/
                  ("RASTER" uchar)*;  /* Supported CCP Event Channel Names of this DAQ List*/
                  ("EXCLUSIVE" int)*;  /* Exclusion of other DAQ-Lists*/
                  "REDUCTION_ALLOWED" ;  /* Data reduction possible*/
                  "FIRST_PID" uchar;  /* First Packet ID (PID) of the DAQ List*/
                }};
              }};
            }};  /* optional parameters for this DAQ list  */
          }})*;  /* DAQ list description*/
          (block "RASTER" struct {{
            char[101];  /* CCP Event Channel Name*/
            char[9];  /* Short Display Name of the Event Channel Name*/
            uchar;  /* Event Channel No., used for CCP START_STOP*/
            int;  /* Period definition :  basic scaling unit in CSE as defined in ASAP1b*/
            long;  /* ECU sample rate of the event channel*/
            taggedstruct {{
              ("EXCLUSIVE" uchar)*;  /* Exclusion of other CCP Event Channels*/
            }};  /* optional parameters*/
          }})*;  /* Event channel description*/
          (block "EVENT_GROUP" struct {{
            char[101];  /* Event group name*/
            char[9];  /* Short name for the event group*/
            taggedstruct {{
              ("RASTER" uchar)*;  /* event channel number*/
            }};  /* list of all event channels beloging to the group*/
          }})*;  /* Group several event channels to form one combined event*/
          block "SEED_KEY" struct {{
            char[256];  /* Name of the Seed&Key DLL for CAL Priviledge, including file-Extension without path*/
            char[256];  /* Name of the Seed&Key DLL for DAQ Priviledge, including file-Extension without path*/
            char[256];  /* Name of the Seed&Key DLL for PGM Priviledge, including file-Extension without path*/
          }};  /* Description of the authentification process*/
          block "CHECKSUM" struct {{
            char[256];  /* Name of the Checksum DLL representing the ECU Algorithm, including file-Extension without path*/
          }};  /* Description of the checksum calculation process*/
          block "TP_BLOB" struct {{
            uint;  /* CCP Version, High Byte: Version, Low Byte : subversion (dec.)*/
            uint;  /* Blob-Version, High Byte: Version, Low Byte : subversion (dec.)*/
            ulong;  /* CAN-Message ID for 'Transmitting to ECU (CRM)'*/
            ulong;  /* CAN-Message ID for 'Receiving from ECU (DTM)'*/
            uint;  /* Logical CCP-Address of the (station address)*/
            uint;  /* Byte order of Multiple-byte-items 1 = high Byte first, 2 = low byte first*/
            taggedstruct {{
              block "CAN_PARAM" struct {{
                uint;  /* Quartz freq. of the elec. control unit */
                uchar;  /* BTR0*/
                uchar;  /* BTR1*/
              }};  /* CAN parameters*/
              "BAUDRATE" ulong;  /* Baud rate in Hz.*/
              "SAMPLE_POINT" uchar;  /* sampling point of time in percent*/
              "SAMPLE_RATE" uchar;  /* number of samples per Bit (1 oder 3)*/
              "BTL_CYCLES" uchar;  /* number of BTL-cycles*/
              "SJW" uchar;  /* SJW-parameter in BTL-cycles*/
              "SYNC_EDGE" enum {{
                "SINGLE" = 0,
                "DUAL" = 1
              }};  /* Synchronisation (SINGLE: on falling edge only, DUAL: on falling and rising edge)*/
              "DAQ_MODE" enum {{
                "ALTERNATING" = 0,
                "BURST" = 1
              }};  /* mode of cylcic data acquisition (ALTERNATING: ECU is sending one ODT per cycle, BURST: ECU is sending a complete DAQ)*/
              "BYTES_ONLY" ;  /* ECU supports max. elements of one Byte size otherwise ECU supports different dataTypes*/
              "RESUME_SUPPORTED" ;  /* ECU supports the Resume function*/
              "STORE_SUPPORTED" ;  /* ECU supports the Store function*/
              "CONSISTENCY" enum {{
                "DAQ" = 0,
                "ODT" = 1
              }};  /* guaranteed consistency (DAQ: consistency of a complete DAQ is guaranteed, ODT: consistency of a complete ODT is guaranteed)*/
              "ADDRESS_EXTENSION" enum {{
                "DAQ" = 0,
                "ODT" = 1
              }};  /* ECU supported address extension (DAQ: only one address extension within a DAQ, ODT: only one address extension within an ODT)*/
              block "CHECKSUM_PARAM" struct {{
                uint;  /* checksum calculation procedure standard types not yet defined, if greater of equal 1000 : manufacturer specific */
                ulong;  /* Maximum block length used by an ASAP1a-CCP command, for checksum calculation procedure */
                taggedstruct {{
                  "CHECKSUM_CALCULATION" enum {{
                    "ACTIVE_PAGE" = 0,
                    "BIT_OR_WITH_OPT_PAGE" = 1
                  }};  /* checksum calculation method*/
                }};  /* optional*/
              }};  /* checksum parameters */
              (block "DEFINED_PAGES" struct {{
                struct {{
                  uint;  /* Logical No. of the memory page (1,2,..)*/
                  char[101];  /* Name of the memory page*/
                  uint;  /* Adress-Extension of the memory page (only Low Byte significant)*/
                  ulong;  /* Base address of the memory page*/
                  ulong;  /* Length of the memory page in Bytes*/
                }};  /* fixed parameters*/
                taggedstruct {{
                  "RAM" ;  /* memory page in RAM*/
                  "ROM" ;  /* memory page in ROM*/
                  "FLASH" ;  /* memory page in FLASH*/
                  "EEPROM" ;  /* memory page in EEPROM*/
                  "RAM_INIT_BY_ECU" ;  /* memory page is initialised by ECU start-up*/
                  "RAM_INIT_BY_TOOL" ;  /* RAM- memory page is initialised by the MCS*/
                  "AUTO_FLASH_BACK" ;  /* RAM memory page is automatically flashed back*/
                  "FLASH_BACK" ;  /* feature available to flash back the RAM memory page*/
                  "DEFAULT" ;  /* memory page is standard (fallback mode)*/
                }};  /* optional parameters */
              }})*;  /* memory page description */
              ("OPTIONAL_CMD" uint)*;  /* CCP-Code of the optional command available*/
            }};  /* optional parameters */
          }};
          ("ADDR_MAPPING" struct {{
            ulong;  /* from*/
            ulong;  /* to*/
            ulong;  /* length*/
          }})*;  /* address mapping for memory segment*/
          "DP_BLOB" struct {{
            uint;  /* Address extension of the calibration data (only Low Byte significant)*/
            ulong;  /* Base address of the calibration data*/
            ulong;  /* Number of Bytes belonging to the calibration data */
          }};  /* for CHARACTERISTIC and AXIS_PTS and MEMORY_LAYOUT*/
          "KP_BLOB" struct {{
            uint;  /* Address extension of the online data (only Low Byte significant)*/
            ulong;  /* Base address of the online data  */
            ulong;  /* Number of Bytes belonging to the online data (1,2 or 4)*/
            taggedstruct {{
              ("RASTER" uchar)*;  /* Array of event channel initialization values*/
            }};
          }};  /* for MEASUREMENT*/
        }};
      }};

    /end A2ML

    /begin MOD_COMMON  "Mod Common Comment Here"
      BYTE_ORDER     MSB_LAST
      ALIGNMENT_BYTE 1
      ALIGNMENT_WORD 2
      ALIGNMENT_LONG 4
      ALIGNMENT_FLOAT32_IEEE 4
      ALIGNMENT_FLOAT64_IEEE 8
      ALIGNMENT_INT64 4
    /end MOD_COMMON

    /begin IF_DATA ASAP1B_CCP
      /begin SOURCE
        "10 ms"
        3
        10
        DISPLAY_IDENTIFIER "DAQ0"
        /begin QP_BLOB
          0x00
          LENGTH 0x05
          CAN_ID_FIXED 0x0501
          RASTER 0x00
          REDUCTION_ALLOWED
          FIRST_PID 0x00
        /end QP_BLOB
      /end SOURCE
      /begin SOURCE
        "50 ms"
        3
        50
        DISPLAY_IDENTIFIER "DAQ1"
        /begin QP_BLOB
          0x01
          LENGTH 0x05
          CAN_ID_FIXED 0x0501
          RASTER 0x01
          REDUCTION_ALLOWED
          FIRST_PID 0x05
        /end QP_BLOB
      /end SOURCE
      /begin SOURCE
        "100 ms"
        3
        100
        DISPLAY_IDENTIFIER "DAQ2"
        /begin QP_BLOB
          0x02
          LENGTH 0x05
          CAN_ID_FIXED 0x0501
          RASTER 0x02
          REDUCTION_ALLOWED
          FIRST_PID 0xA
        /end QP_BLOB
      /end SOURCE
      /begin RASTER
        "Channel 0"
        "Channel 0"
        0x00
        3
        10
      /end RASTER
      /begin RASTER
        "Channel 1"
        "Channel 1"
        0x01
        3
        50
      /end RASTER
      /begin RASTER
        "Channel 2"
        "Channel 2"
        0x02
        3
        100
      /end RASTER
      /begin SEED_KEY
        ""
        ""
        ""
      /end SEED_KEY
      /begin TP_BLOB
        0x0201
        0x0206
        0x0510
        0x0501
        0x1234
        0x02
        DAQ_MODE BURST
        CONSISTENCY ODT
        ADDRESS_EXTENSION DAQ
        BYTES_ONLY
        /begin CHECKSUM_PARAM
          0x8006
          0x1000
        /end CHECKSUM_PARAM
        /begin DEFINED_PAGES
          0x01
          "Reference Page"
          0x00
          0x5C4000
          0x10000
          ROM
        /end DEFINED_PAGES
        /begin DEFINED_PAGES
          0x02
          "Working Page"
          0x00
          0x20000000
          0x10000
          RAM
          RAM_INIT_BY_ECU
        /end DEFINED_PAGES
        OPTIONAL_CMD 0x08
        OPTIONAL_CMD 0x09
        OPTIONAL_CMD 0x0C
        OPTIONAL_CMD 0x0D
        OPTIONAL_CMD 0x0E
        OPTIONAL_CMD 0x11
		OPTIONAL_CMD 0x10
		OPTIONAL_CMD 0x18
		OPTIONAL_CMD 0x22
		
        BAUDRATE 0x07A120
        SAMPLE_POINT 0x4B
        SAMPLE_RATE 0x01
        BTL_CYCLES 0x08
        SJW 0x02
        SYNC_EDGE SINGLE
      /end TP_BLOB
    /end IF_DATA
    /begin IF_DATA CANAPE_ADDRESS_UPDATE
    /end IF_DATA

    /begin MOD_PAR ""
      VERSION "1.2"
      SUPPLIER "YKTC"
      CUSTOMER "DeepWay"
      ECU "FBCM"
      CPU_TYPE "S32K344"
    
    /begin MEMORY_SEGMENT
        code1 "Internal flash Space"
        CODE FLASH INTERN 0x400000 0x1C4000 -1 -1 -1 -1 -1
     /end MEMORY_SEGMENT	
      
	 /begin MEMORY_SEGMENT
        cal1 "Internal Data Flash CAL Space"
        DATA FLASH INTERN 0x5C4000 0x10000 -1 -1 -1 -1 -1
      /end MEMORY_SEGMENT  
	  
	  /begin MEMORY_SEGMENT
        cal1 "Internal Data RAM CAL Space"
        DATA RAM INTERN 0x20000000 0x10000 -1 -1 -1 -1 -1
      /end MEMORY_SEGMENT
	  
    /end MOD_PAR

'''

g_a2l_text_back_content = '''
    /begin COMPU_METHOD
        /* Name of CompuMethod    */      SXXT_01_FBCM_CM_single
        /* Long identifier        */      "Q = V"
        /* Conversion Type        */      RAT_FUNC
        /* Format                 */      "%8.6"
        /* Units                  */      ""
        /* Coefficients           */      COEFFS 0 1 0 0 0 1
    /end COMPU_METHOD

    /begin COMPU_METHOD
        /* Name of CompuMethod    */      SXXT_01_FBCM_CM_uint8_enum
        /* Long identifier        */      "Q = V"
        /* Conversion Type        */      RAT_FUNC
        /* Format                 */      "%3.0"
        /* Units                  */      "enum"
        /* Coefficients           */      COEFFS 0 1 0 0 0 1
    /end COMPU_METHOD

    /begin COMPU_METHOD
        /* Name of CompuMethod    */      SXXT_01_FBCM_CM_uint32_null
        /* Long identifier        */      "Q = V"
        /* Conversion Type        */      RAT_FUNC
        /* Format                 */      "%10.0"
        /* Units                  */      "null"
        /* Coefficients           */      COEFFS 0 1 0 0 0 1
    /end COMPU_METHOD

    /begin  RECORD_LAYOUT Scalar_BOOLEAN
    FNC_VALUES 1 UBYTE COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_FLOAT32_IEEE
      FNC_VALUES 1 FLOAT32_IEEE COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_FLOAT64_IEEE
      FNC_VALUES 1 FLOAT64_IEEE COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_SLONG
      FNC_VALUES 1 SLONG COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_UBYTE
      FNC_VALUES 1 UBYTE COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_ULONG
      FNC_VALUES 1 ULONG COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

    /begin  RECORD_LAYOUT Scalar_UWORD
      FNC_VALUES 1 UWORD COLUMN_DIR DIRECT
    /end  RECORD_LAYOUT 

  /end MODULE

/end PROJECT
/* (add any file trailer comments here) */
/* EOF SXXT_01_FBCM.a2l*/
'''

def get_script_dir():
    if getattr(sys, 'frozen', False):
        # If pyinstaller(exe)
        return os.path.dirname(sys.executable)
    else:
        # If py
        return os.path.dirname(os.path.abspath(__file__))

def load_config(config_file='config.json'):
    with open(config_file, 'r') as f:
        return json.load(f)

# switch work dir
script_dir = get_script_dir()
os.chdir(script_dir)
print(f"{script_dir}")

def get_elf_addresses(elf_file):
    gl0906_elf = gl0906_elf_parse(elf_file)
    # gl0906_elf.print_elf_info()
    gl0906_elf.get_elf_variable_address()
    # gl0906_elf.extract_structs_addr_from_dwarf()
    gl0906_elf.close_elf_file()

    print(f"elf addr parse finish!!!")
    return gl0906_elf.update_addr

def get_calibration_data(calibration_file, update_addr):
    # Sample content of a C file with calibration definitions
    with open(calibration_file, 'r', encoding='gbk') as file:
        c_file_content = file.read()

    # Regular expression to match the calibration definition in the C file
    # This pattern matches the data type, variable name, and the following comment
    calibration_pattern = re.compile(r"const\s+(\w+)\s+(\w+)")

    # Dictionary to store the parsed results
    calibrations = {}

    # Search for all matches using the regular expression
    for match in calibration_pattern.finditer(c_file_content):
        # match.groups() returns a tuple containing all the captured groups
        group = match.groups()

        if update_addr[group[1]] is None:
            print(f"{group[1]} do not have addr in elf!")
            exit(1)
        else:
            # Store the parsed results in the dictionary
            calibrations[group[1]] = update_addr[group[1]] - 0x1FA3C000

    # Template for constructing the A2L file content
    a2l_template = """
    /begin CHARACTERISTIC 
        /* Name                   */      {calibration_name}
        /* Long Identifier        */      ""
        /* Type                   */      VALUE
        /* ECU Address            */      {ecu_address}
        /* Record Layout          */      Scalar_UBYTE
        /* Maximum Difference     */      0
        /* Conversion Method      */      SXXT_01_FBCM_CM_uint8_enum
        /* Lower Limit            */      0.000000
        /* Upper Limit            */      255.000000
    /end CHARACTERISTIC
    """

    # Fill in the A2L template and generate the final A2L content
    a2l_content = ""
    for var_name, addr in calibrations.items():
        # Replace the placeholders in the template with actual values
        a2l_entry = a2l_template.format(
            calibration_name=var_name,
            ecu_address=hex(addr)  # Replace with the actual ECU address
        )
        a2l_content += a2l_entry

    # print(a2l_content)
    return a2l_content

def get_measurement_data(measurement_file, update_addr):
    # Sample content of a C file with measurement_file definitions
    with open(measurement_file, 'r', encoding='gbk') as file:
        c_file_content = file.read()

    # Regular expression to match the measurement_file definition in the C file
    # This pattern matches the data type, variable name, and the following comment
    measurement_pattern = re.compile(r"\s*(volatile)?\s*(\w+)\s+(\w+);")

    # Dictionary to store the parsed results
    measurements = {}

    # Search for all matches using the regular expression
    for match in measurement_pattern.finditer(c_file_content):
        # match.groups() returns a tuple containing all the captured groups
        group = match.groups()

        if update_addr[group[2]] is None:
            print(f"{group[2]} do not have addr in elf!")
            exit(1)
        else:
            if "float32" == group[1] or "float32_t" == group[1]:
                dtype = "FLOAT32_IEEE"
                method = "SXXT_01_FBCM_CM_single"
                lower = "-1000.000000"
                upper = "1000.000000"
            elif "uint8" == group[1] or "uint8_t" == group[1]:
                dtype = "UBYTE"
                method = "SXXT_01_FBCM_CM_uint8_enum"
                lower = "0"
                upper = "255"
            elif "uint32" == group[1] or "uint32_t" == group[1]:
                dtype = "VALUE"
                method = "SXXT_01_FBCM_CM_uint32_null"
                lower = "0.000000"
                upper = "4294967295.000000"
            else:
                print(f"{group}")
                print(f"{group[2]} data type error!")
                exit()
            

            # Store the parsed results in the dictionary
            measurements[group[2]] = {
                "addr": update_addr[group[2]],
                "type": dtype,
                "method": method,
                "lower": lower,
                "upper": upper
            }

    # Template for constructing the A2L file content
    a2l_template = """
    /begin MEASUREMENT 
        /* Name                   */      {measurement_name}
        /* Long identifier        */      ""
        /* Data type              */      {measurement_type}
        /* Conversion method      */      {measurement_method}
        /* Resolution (Not used)  */      0      
        /* Accuracy (Not used)    */      0      
        /* Lower limit            */      {measurement_lower}
        /* Upper limit            */      {measurement_upper}
        ECU_ADDRESS                       {ecu_address}
    /end MEASUREMENT 
    """

    # Fill in the A2L template and generate the final A2L content
    a2l_content = ""
    for var_name, meas in measurements.items():
        # Replace the placeholders in the template with actual values
        a2l_entry = a2l_template.format(
            measurement_name=var_name,
            measurement_type = meas["type"],
            measurement_method = meas["method"],
            measurement_lower = meas["lower"],
            measurement_upper = meas["upper"],
            ecu_address=hex(meas["addr"])  # Replace with the actual ECU address
        )
        a2l_content += a2l_entry

    # print(a2l_content)
    return a2l_content

def save_updated_a2l(output_file, updated_sections):

    # print(f"{a2l_content}")
    with open(output_file, 'w', encoding='utf-8') as file:
        now_time = datetime.now().strftime('%a %b %d %H:%M:%S %Y')
        a2l_text_front_content = g_a2l_text_front_content.format(
            ddd2 = now_time
        )
        file.write(a2l_text_front_content)
        file.write(updated_sections)
        file.write(g_a2l_text_back_content)
    print(f"write {output_file} finish!!!")


if __name__ == "__main__":
    config = load_config()
    if True == config['logger']['enable']:
        logger = gl0906_logger(log_dir=config['logger']['log_file_path'], max_files=config['logger']['log_file_count'])
        redirect_output_to_log(logger)
        logger.cleanup_logs()

    if True == config['elf_map_a2l']['enable']:
        print("###### Start: map a2l file!")
        update_addr = get_elf_addresses(config['elf_map_a2l']['elf_file'])
        a2l_content = get_calibration_data(config['elf_map_a2l']['calibration_file'], update_addr)
        a2l_content = a2l_content + get_measurement_data(config['elf_map_a2l']['measurement_file'], update_addr)
        save_updated_a2l(config['elf_map_a2l']['a2l_out_file'], a2l_content)
        print("###### Finish: map a2l file!")
    
    if True == config['hex_file_handle']['enable']:
        print("###### Start: merge hex file!")
        dcheck_and_parse_blocks(config['hex_file_handle']['apphex'])
        dcheck_and_parse_blocks(config['hex_file_handle']['a2lhex'])
        src_addr = int(config['hex_file_handle']['a2laddr'], 16)
        src_len = int(config['hex_file_handle']['a2llen'], 16)
        dst_addr = int(config['hex_file_handle']['appaddr'], 16)
        dmerge_hex_data(config['hex_file_handle']['a2lhex'], config['hex_file_handle']['a2lhex'], src_addr, src_len, dst_addr)
        print("###### Finish: merge hex file!")
